package ca.tatham.scheduler.simulate;

import java.util.HashSet;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;

import ca.tatham.scheduler.Log;
import ca.tatham.scheduler.Scheduler;
import ca.tatham.scheduler.Task;

public abstract class AbstractSchedulerSimulation {
	



	protected Object createResource(final int id) {
		return "Resource " + id;
	}

	protected void execute(TaskDiagram diagram, final Task[] tasks,
			final int[] sleeps) throws InterruptedException {

		Log.log(this, "Starting Test");
		Scheduler scheduler = Scheduler.instance();
		for (Task task : tasks) {
			scheduler.registerTask(task);
		}

		final Object blocker = new Object();

		Thread tester = new Releaser(blocker, diagram, tasks, sleeps);
		tester.setDaemon(false);
		tester.start();
		synchronized (blocker) {
			blocker.wait();
		}
		System.out.print(diagram.getDiagram());
	}
}

final class Releaser extends Thread implements Observer {

	private final Object m_blocker;
	private final int[] m_unitsToWait;
	private final Task[] m_tasks;
	private final Set<Task> m_scheduledTasks = new HashSet<Task>();
	private int m_unitsDone = 0;

	public Releaser(Object blocker, TaskDiagram diagram, Task[] tasks,
			int[] sleeps) {
		super("Starter");
		setPriority(getThreadGroup().getMaxPriority() - 1);
		Scheduler.instance().addObserver(this);
		diagram.addObserver(new Observer() {
			public void update(@SuppressWarnings("unused")
			Observable o, @SuppressWarnings("unused")
			Object arg) {
				unit();
			}

		});
		m_blocker = blocker;
		m_unitsToWait = sleeps;
		m_tasks = tasks;
	}

	protected void unit() {
		for (int i = 0; i < m_tasks.length; i++) {
			if (m_unitsToWait[i] == m_unitsDone) {
				if (!m_scheduledTasks.contains(m_tasks[i])) {
					m_scheduledTasks.add(m_tasks[i]);
					Scheduler.schedule(m_tasks[i]);
				}
			}
		}
		m_unitsDone++;
	}

	public void run() {
		unit();
		Log.log(this, "Exiting Tester...");
	}

	public void update(@SuppressWarnings("unused")
	Observable o, @SuppressWarnings("unused")
	Object arg) {
		synchronized (m_blocker) {
			Log.log(this, "Unblocking main thread!");
			m_blocker.notify();
		}
	}
}
